package com.zkh.myframe.database.mapper;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.zkh.myframe.database.mapper.parser.ParamValue;
import com.zkh.myframe.database.mapper.parser.ParamValueType;
import com.zkh.myframe.database.mapper.parser.ParseUtils;
import com.zkh.myframe.database.mapper.tag.Foreach;
import com.zkh.myframe.database.mapper.tag.If;
import com.zkh.myframe.database.mapper.tag.Include;
import com.zkh.myframe.database.mapper.tag.Tag;
import com.zkh.myframe.database.mapper.tag.Trim;
import com.zkh.myframe.database.mapper.tag.Where;
import com.zkh.myutils.expression.Expression;
import com.zkh.myutils.utils.Regexp;
import com.zkh.myutils.utils.Regexp.ResultSet;
import com.zkh.myutils.utils.StringUtils;
import com.zkh.myutils.utils.Utils;

/**
 * SQL实体
 * @author zkh
 */
public class SQLEntity extends Tag {

	//参数
	private List<Object> paramList = new ArrayList<>();
	
	//命名空间
	private String namespace;
	//ID
	private String id;
	//操作类型
	private String operType;
	//数据源
	private String dataSource;
	
	/**
	 * 构造器
	 * @param namespace 命名空间
	 * @param id SQL语句ID
	 */
	public SQLEntity(String namespace, String id) {
		this.namespace = namespace;
		this.id = id;
	}
	
	/**
	 * 构造器
	 * @param namespace 命名空间
	 * @param id SQL语句ID
	 * @param dataSource 数据源ID
	 */
	public SQLEntity(String namespace, String id, String dataSource) {
		this.namespace = namespace;
		this.id = id;
		this.dataSource = dataSource;
	}
	
	/**
	 * 获取命名空间
	 */
	public String getNamespace() {
		return namespace;
	}
	/**
	 * 获取ID
	 */
	public String getId() {
		return id;
	}

	/**
	 * 获取组装SQL语句
	 */
	public String getSql(Map<String, Object> paramMap) {
		//临时SQL
		String sql = getEachSQL(this, paramMap);
		//清空参数，防止重复调用时设置重复参数
		this.paramList.clear();
		//解析SQL中的#{}${}表达式
		List<ResultSet> rsList = Regexp.getResultSet("[#\\$]\\{.+?\\}", sql);
		//从后面往前面遍历（替换字符串保证前面的索引不变）
		for(int i=rsList.size()-1;i>=0;i--) {
			//当前匹配值
			ResultSet rs = rsList.get(i);
			//获取值
			ParamValue pv = ParamValue.parseValue(rs.getResult());
			//获取值
			Object val = null;
			//分类型获取值
			if(pv.getType()==ParamValueType.FIXVALUE) {
				val = pv.getValue();
			}else if(pv.getType()==ParamValueType.STATICFIELD) {
				val = ParseUtils.getFieldValue(pv.getExpression().split("@"));
			}else {
				val = Utils.parseValue(pv.getExpression(), paramMap);
			}
			//替换参数占位符
			sql = sql.substring(0, rs.getStart()) + (pv.isPlaceholder() ? "?" : val) + sql.substring(rs.getEnd());
			//占位符
			if(pv.isPlaceholder()) {
				this.paramList.add(0, val);
			}
		}
		//返回SQL
		return sql.replaceAll("  +", " ").trim();
	}
	
	/**
	 * 遍历获取SQL
	 * @param tag 待解析的标签对象，可为include，where，foreach，if，trim
	 * @param paramMap 参数容器，用来存放参数
	 */
	private String getEachSQL(Tag tag, Map<String, Object> paramMap) {
		return getEachSQL(tag, paramMap, null, null, null);
	}
	
	/**
	 * 遍历获取SQL
	 * @param tag 待解析的标签对象，可为include，where，foreach，if，trim
	 * @param paramMap 参数容器，用来存放参数
	 * @param item item变量名。仅用作解析foreach标签。
	 * @param index index变量名。仅用作解析foreach标签。
	 * @param unique 唯一标识。仅用作解析foreach标签。
	 */
	private String getEachSQL(Tag tag, Map<String, Object> paramMap, String item, String index, String unique) {
		//临时SQL
		String sql = tag.getOriginSQL().toString().trim();
		//获取标签占位符
		List<String> tags = Regexp.getMatchesAll("\\{:(include|where|foreach|if|trim)\\}", sql);
		//计数器
		int include=0,where=0,foreach=0,IF=0,trim=0;
		try {
			//遍历占位符
			for(String ctag: tags) {
				//分别调用不同的解析方法
				switch(ctag) {
					case "{:include}":
						sql = sql.replaceFirst("\\{:include\\}", parseInclude(tag.includes.get(include++), paramMap));break;
					case "{:where}":
						sql = sql.replaceFirst("\\{:where\\}", parseWhere(tag.wheres.get(where++), paramMap));break;
					case "{:foreach}":
						sql = sql.replaceFirst("\\{:foreach\\}", parseForeach(tag.foreachs.get(foreach++), paramMap));break;
					case "{:if}":
						sql = sql.replaceFirst("\\{:if\\}", parseIf(tag.ifs.get(IF++), paramMap));break;
					default:
						sql = sql.replaceFirst("\\{:trim\\}", parseTrim(tag.trims.get(trim++), paramMap));break;
				}
			}
		}catch(IndexOutOfBoundsException e) {
			throw new MapperParseException("解析Mapper出错", e);
		}
		//如果是foreach
		if(!StringUtils.isExistEmpty(item, index, unique)) {
			//替换变量名
			sql = sql.replaceAll("([#\\$]\\{ *)"+item+"( *\\})", "$1"+item+unique+"$2").replaceAll("([#\\$]\\{ *)"+index+"( *\\})", "$1"+index+unique+"$2");
		}
		//返回SQL
		return sql;
	}
	
	/**
	 * 获取组装的参数
	 */
	public Object[] getParams() {
		return paramList.toArray();
	}

	/**
	 * 获取操作类型。包含select，insert，update，delete
	 */
	public String getOperType() {
		return operType;
	}

	/**
	 * 设置操作类型
	 * @param operType 操作类型
	 */
	public SQLEntity renderOperType(String operType) {
		this.operType = operType;
		return this;
	}
	/**
	 * 获取数据源
	 */
	public String getDataSource() {
		return dataSource;
	}

	@Override
	public String toString() {
		return "SQLEntity [namespace=" + namespace + ", id=" + id + ", includes=" + includes + ", foreachs=" + foreachs
				+ ", wheres=" + wheres + ", ifs=" + ifs + ", trims=" + trims + ", originSQL=" + originSQL + "]";
	}
	
	/**
	 * 
	 * 解析Include标签
	 * @param includeTag include标签
	 * @param paramMap Mapper接口方法传递的参数
	 */
	private String parseInclude(Include includeTag, final Map<String, Object> paramMap) {
		//引用SQL标签
		SQLEntity entity = includeTag.getSqlEntity();
		//参数汇总
		for(Entry<String, ParamValue> kv : includeTag.getParamMap().entrySet()) {
			switch(kv.getValue().getType()) {
				case FIXVALUE:		//固定值，直接取value值
					paramMap.put(kv.getKey(), kv.getValue().getValue());
					break;
				case STATICFIELD:	//static修饰的属性值
					//获取类名和方法
					String[] elems = kv.getValue().getExpression().split("@");
					//取值
					paramMap.put(kv.getKey(), ParseUtils.getFieldValue(elems[0], elems[1]));
					break;
				case VARIABLE:		//变量值，通过参数获取
					paramMap.put(kv.getKey(), Utils.parseValue(kv.getValue().getExpression(), paramMap));
					break;
			}
		}
		//获取SQL并返回
		return getEachSQL(entity, paramMap);
	}
	
	/**
	 * 解析Where标签
	 * @param where where标签
	 * @param paramMap Mapper接口方法传递的参数
	 */
	private String parseWhere(Where where, Map<String, Object> paramMap) {
		//获取SQL
		String sql = getEachSQL(where, paramMap);
		//是否为空
		if(StringUtils.isNotEmpty(sql)) {
			sql = " where " + (Regexp.isStartWith("(?i)(and|or)\\s", sql) ? sql.substring(3).trim() : sql);
		}
		return sql;
	}
	
	/**
	 * 解析Foreach标签
	 * @param foreach foreach标签
	 * @param paramMap Mapper接口方法传递的参数
	 */
	private String parseForeach(Foreach foreach, Map<String, Object> paramMap) {
		//foreach配置的临时变量
		String item = foreach.getItem().trim();
		String index = foreach.getIndex().trim();
		//SQL片段
		List<String> sqls = new ArrayList<>();
		//获取集合或数组
		Object value = Utils.parseValue(foreach.getCollection().trim(), paramMap);
		//如果为空，返回空字符串
		if(value==null) {
			return "";
		}
		//唯一标识
		String unique = "_" + Utils.getUUID() + "_";
		//Class
		Class<?> cls = value.getClass();
		//数组
		if(cls.isArray()) {
			//长度
			int len = Array.getLength(value);
			//遍历
			for(int i=0;i<len;i++) {
				//当前值
				Object v = Array.get(value, i);String u = unique + i;
				//赋值参数
				paramMap.put(item + u, v);
				paramMap.put(index + u, i);
				//设置临时参数值，为获取当前item和index供数
				paramMap.put(item, v);paramMap.put(index, i);
				//获取SQL
				String sql = getEachSQL(foreach, paramMap, item, index, u);
				//保存SQL
				if(StringUtils.isNotEmpty(sql)) {
					sqls.add(sql);
				}
			}
		}//集合
		else if(value instanceof Collection) {
			//计数
			int i = 0;
			//遍历集合
			for(Object v: (Collection<?>) value) {
				//唯一标识
				String u = unique + i;
				//赋值参数
				paramMap.put(item + u, v);
				paramMap.put(index + u, i++);
				//设置临时参数值，为获取当前item和index供数
				paramMap.put(item, v);paramMap.put(index, i);
				//SQL
				String sql = getEachSQL(foreach, paramMap, item, index, u);
				//保存SQL
				if(StringUtils.isNotEmpty(sql)) {
					sqls.add(sql);
				}
			}
		}else {
			throw new MapperParseException("foreach标签仅支持数组和Collection集合的遍历");
		}
		//生成SQL，默认空
		String sql = "";
		//有SQL
		if(!sqls.isEmpty()) {
			//拼接
			sql = String.join(StringUtils.isEmpty(foreach.getSeparator()) ? "," : foreach.getSeparator(), sqls);
			//拼接开始和结束
			if(StringUtils.isNotEmpty(foreach.getOpen())) {
				sql = foreach.getOpen() + sql;
			}
			if(StringUtils.isNotEmpty(foreach.getClose())) {
				sql += foreach.getClose();
			}
		}
		//返回SQL
		return sql;
	}
	
	/**
	 * 解析If标签
	 * @param IF if标签
	 * @param paramMap Mapper接口方法传递的参数
	 */
	private String parseIf(If IF, Map<String, Object> paramMap) {
		//是否满足条件
		if((boolean) Expression.newInstance(IF.getCondition()).execute(paramMap)) {
			//获取SQL片段
			return getEachSQL(IF, paramMap);
		}
		//elif标签
		List<If> elifs = IF.getElifList();
		//遍历
		for(If elif: elifs) {
			//是否满足条件
			if((boolean) Expression.newInstance(elif.getCondition()).execute(paramMap)) {
				//获取SQL片段
				return getEachSQL(elif, paramMap);
			}
		}
		//else标签
		if(IF.getElseTag()!=null) {
			//获取SQL片段
			return getEachSQL(IF.getElseTag(), paramMap);
		}
		//返回空
		return "";
	}
	
	/**
	 * 解析Trim标签
	 * @param trim trim标签
	 * @param paramMap Mapper接口方法传递的参数
	 */
	private String parseTrim(Trim trim, Map<String, Object> paramMap) {
		//获取SQL
		String sql = getEachSQL(trim, paramMap).trim();
		//去除开始
		if(StringUtils.isNotEmpty(trim.getPrefixTrim()) && sql.startsWith(trim.getPrefixTrim())) {
			sql = sql.substring(trim.getPrefixTrim().length());
		}
		//去除结尾
		if(StringUtils.isNotEmpty(trim.getSuffixTrim()) && sql.endsWith(trim.getSuffixTrim())) {
			sql = sql.substring(0, sql.length()-trim.getSuffixTrim().length());
		}
		//拼接开始
		if(StringUtils.isNotEmpty(trim.getPrefixAppend())) {
			sql = " " + trim.getPrefixAppend() + " " + sql;
		}
		//拼接结尾
		if(StringUtils.isNotEmpty(trim.getSuffixAppend())) {
			sql += " " + trim.getSuffixAppend() + " ";
		}
		//返回SQL
		return sql;
	}
}
